#ifndef COMPILER_H
#define COMPILER_H

#include <stdio.h>
#include <stdbool.h>
#include <string.h>

// 比较字符串的宏函数
#define S_EQ(str1, str2) \
    (str1 && str2 && (strcmp(str1, str2) == 0))

// 代码的行号和列号
struct position
{
    int line;
    int col;
    const char *filename;
};

// 数字令牌
#define NUMBER_CASE \
    case '0':       \
    case '1':       \
    case '2':       \
    case '3':       \
    case '4':       \
    case '5':       \
    case '6':       \
    case '7':       \
    case '8':       \
    case '9'

// 不包含除法的操作符令牌
#define OPERATOR_CASE_EXCLUDING_DIVISION \
    case '+':                            \
    case '-':                            \
    case '*':                            \
    case '<':                            \
    case '>':                            \
    case '^':                            \
    case '%':                            \
    case '!':                            \
    case '=':                            \
    case '~':                            \
    case '|':                            \
    case '&':                            \
    case '(':                            \
    case '[':                            \
    case ',':                            \
    case '.':                            \
    case '?'

// 结束符
#define SYMBOL_CASE \
    case '{':       \
    case '}':       \
    case ':':       \
    case ';':       \
    case '#':       \
    case '\\':      \
    case ')':       \
    case ']'

// 词法分析枚举类型
enum
{
    LEXICAL_ANALYSIS_ALL_OK,
    LEXICAL_ANALYSIS_INPUT_ERROR,
};

// token类型枚举
enum
{
    TOKEN_TYPE_IDENTIFIER,
    TOKEN_TYPE_KEYWORD,
    TOEKN_TYPE_OPERATOR,
    TOKEN_TYPE_SYMBOL,
    TOKEN_TYPE_NUMBER,
    TOKEN_TYPE_STRING,
    TOKEN_TYPE_COMMENT,
    TOKEN_TYPE_NEWLINE,
};

// token结构体
struct token
{
    int type;
    int flags;
    union
    {
        char cval;
        const char *sval;
        unsigned int inum;
        unsigned long lnum;
        unsigned long long llnum;
        void *any;
    };
    // token 的位置信息
    struct position pos;

    // token 与token之间是否有空白符（true：有）
    bool whitespace;

    // 将括号中的代码视为一个整体，指向在括号之间的代码缓冲区的开始
    const char *between_brackets;
};

struct lex_process;
// 定义一个指向函数，此函数接收一个指向lex_process的指针作为参数【用于从源代码中获取下一个字符】
typedef char (*LEX_PROCESS_NEXT_CHAR)(struct lex_process *process);
// 用于查看下一个字符而不移动指针位置
typedef char (*LEX_PROCESS_PEEK_CHAR)(struct lex_process *process);
// 用于将字符推回到输入流中
typedef void (*LEX_PROCESS_PUSH_CHAR)(struct lex_process *process, char c);

//
struct lex_process_functions
{
    LEX_PROCESS_NEXT_CHAR next_char;
    LEX_PROCESS_PEEK_CHAR peek_char;
    LEX_PROCESS_PUSH_CHAR push_char;
};

struct lex_process
{
    // 当前所在行和列
    struct position pos;

    struct vector *token_vec;
    struct compile_process *compiler;

    // 当前表达式数量
    int current_expression_count;
    // 括号缓冲区
    struct buffer *parentheses_buffer;
    struct lex_process_functions *function;

    void *private;
};

// 编译过程枚举
enum
{
    COMPILER_FAILED_WITH_ERRORS,
    COMPILER_FILE_COMPILED_OK,
};

// 编译过程struct
struct compile_process
{
    // 如何编译此文件的标志
    int flags;
    // 位置
    struct position pos;
    // 输入文件子结构【c文件编译文件】
    struct compile_process_input_file
    {
        // 文件指针
        FILE *fp;
        // 绝对路径
        const char *abs_path;
    } cfile;

    // 输出文件位置
    FILE *ofile;
};

/**
 * 编译函数
 * filename:待编译文件名
 * out_filename:输出文件名
 * flags:如何编译此文件的标志
 **/
int compile_file(const char *filename, const char *out_filename, int flags);

/**
 * 创建编译过程函数
 * filename:待编译文件名
 * out_filename:输出文件名
 * flags:如何编译此文件的标志
 * Returns:成功：compile_file，失败：NULL
 **/
struct compile_process *compile_process_create(const char *filename, const char *out_filename, int flags);

// 获取下一个字符，并更新当前正在处理的位置
char compile_process_next_char(struct lex_process *process);

// 窥视下一个字符，不移动文件指针
char compile_process_peek_char(struct lex_process *process);

// 将字节退回输入流中，以便后续能够再次读取
void compile_process_push_char(struct lex_process *process, char c);

// 创建并初始化lex_process实例
struct lex_process *lex_process_create(struct compile_process *compiler, struct lex_process_functions *functions, void *private);

// 释放lex_process实例所占用的资源
void lex_process_free(struct lex_process *process);

// 访问lex_process实例中私有数据的接口
void *lex_process_private(struct lex_process *process);

// 访问lex_process实例中词法单元向量的接口
struct vector *lex_process_vector(struct lex_process *process);

// 词法分析
int lex(struct lex_process *process);

// 编译错误打印
void compile_error(struct compile_process *compiler, const char *msg, ...);

// 编译警告打印
void compile_warning(struct compile_process *compiler, const char *msg, ...);

// token是否为关键字类型
bool token_is_keyword(struct token *token, const char *value);
#endif